package com.example.demo19_httpfirewall.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * 需要注意的是，Security 过滤器并不是直接放在 Web 项目的原生过滤器链中，⽽是通过一个 FilterChainProxy 来统一管理。
 * FilterChainProxy 本身是通过 Spring 框架提供的 DelegatingFilterProxy 整合到原生的过滤器链中。
 *
 * 在 FilterChainProxy 中的 doFilter -> doFilterInternal 方法
 * 1. 先处理 HttpFirewall 默认实现 StrictHttpFirewall 的校验逻辑
 *
 * 2. virtualFilterChain.doFilter 处理 Spring Security 中的内部过滤器
 *
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("wangnian").password("123").roles("admin").build());
        return inMemoryUserDetailsManager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().formLogin()
                .and().csrf().disable()
                .sessionManagement()
                // 防御会话固定攻击，sessionFixation 四种策略，
                // migrateSession 表示在登录成功之后，创建一个新的会话，然后讲旧的 session 中的信息复制到新的 session 中。
                // none 表示不做任何事情，继续使用旧的 session。
                // changeSessionId 表示 session 不变，但是会修改 sessionId，这实际上用到了 Servlet 容器提供的防御会话固定攻击，「默认即此」。
                // newSession 表示 在 migrateSession 基础上将旧的 session 的最大非活动间隔（setMaxInactiveInterval）设置到新的 session 中。
                .sessionFixation().changeSessionId();
    }
}
